package controller

import (
	"context"
	"fmt"

	"github.com/rs/zerolog"
	"gitlab.com/go-course-project/go17/book/config"
	"gitlab.com/go-course-project/go17/book/exception"
	"gitlab.com/go-course-project/go17/book/model"
	"gorm.io/gorm"
)

// 构造函数, 用户初始化这个结构体
func NewBookController() *BookController {
	return &BookController{
		db:     config.Get().MySQL.DB(),
		logger: config.Get().Log.Logger(),
	}
}

type BookController struct {
	db     *gorm.DB
	logger *zerolog.Logger
}

type GetBookRequest struct {
	Isbn int64
	// ...
	// TraceId string
}

// 怎么定义你的Controller的参数
// 怎么定义你BookController的接口(interface)
// 函数名称 + 参数 +  返回 + 异常
// 参数 一定要 注意 兼容性
// 1.参数类型，个数
// 怎么设计一个 具有良好兼容性的接口, 个数，类型
// 采用一个请求类型 作为参数, 变更请求结构体, 不会变更类型，有更好的兼容性
// 1. 非功能性的需求, 要添加TraceID, 请求支持取消
// go context.Context 既能存储数据，也能做Gorouteine的取消
// 这个跟接口是无关的，是纯业务处理
func (c *BookController) GetBook(ctx context.Context, req *GetBookRequest) (*model.Book, error) {
	ins := &model.Book{}

	if err := c.db.WithContext(ctx).Where("isbn = ?", req.Isbn).Take(ins).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, exception.ErrNotFound("%d not found", req.Isbn)
		}
		return nil, fmt.Errorf("get book error, %s", err)
	}

	return ins, nil
}

// 这一层做强教育，避免内部相互调用是，处理未校验的情况
func (c *BookController) CreateBook(ctx context.Context, req *model.BookSpec) (*model.Book, error) {
	if err := req.Validate(); err != nil {
		return nil, err
	}

	//  req 请求信息
	// 1. fmt.Println(req)
	// log.Println()
	// 2. zerolog
	c.logger.Debug().Msgf("req 请求信息: %s", req)

	// book, save
	ins := &model.Book{
		BookSpec: *req,
	}
	if err := c.db.Save(ins).Error; err != nil {
		return nil, err
	}

	return ins, nil
}
